﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;

namespace ABDLight
{
    public class JSONObject
    {

        public static Dictionary<string, string> JSON_Dict { get; set; }

        public static int DictNum { get; set; }
        public static int DictDepth { get; set; }
        public static string Parrent { get; set; }
        /// <summary>
        /// Creates a JSONObject by parsing a string.
        /// This is the only correct way to create a JSONObject.
        /// </summary>
        public static JSONObject CreateFromString(string s)
        {

            object o;
            JavaScriptSerializer js = new JavaScriptSerializer();
            try
            {
                o = js.DeserializeObject(s);
            }
            catch (ArgumentException)
            {
                throw new Exception();
            }

            return Create(o);
        }

        /// <summary>
        /// Returns true if this JSONObject represents a dictionary.
        /// </summary>
        public bool IsDictionary
        {
            get
            {
                return _dictData != null;
            }
        }

        /// <summary>
        /// Returns true if this JSONObject represents an array.
        /// </summary>
        public bool IsArray
        {
            get
            {
                return _arrayData != null;
            }
        }

        /// <summary>
        /// Returns true if this JSONObject represents a string value. 
        /// </summary>
        public bool IsString
        {
            get
            {
                return _stringData != null;
            }
        }

        /// <summary>
        /// Returns true if this JSONObject represents an integer value.
        /// </summary>
        public bool IsInteger
        {
            get
            {
                Int64 tmp;
                return Int64.TryParse(_stringData, out tmp);
            }
        }

        /// <summary>
        /// Returns true if this JSONOBject represents a boolean value.
        /// </summary>
        public bool IsBoolean
        {
            get
            {
                bool tmp;
                return bool.TryParse(_stringData, out tmp);
            }
        }

        /// <summary>
        /// Returns this JSONObject as a dictionary
        /// </summary>
        public Dictionary<string, JSONObject> Dictionary
        {
            get
            {
                return _dictData;
            }
        }

        /// <summary>
        /// Returns this JSONObject as an array
        /// </summary>
        public JSONObject[] Array
        {
            get
            {
                return _arrayData;
            }
        }

        /// <summary>
        /// Returns this JSONObject as a string
        /// </summary>
        public string String
        {
            get
            {
                return _stringData;
            }
        }

        /// <summary>
        /// Returns this JSONObject as an integer
        /// </summary>
        public Int64 Integer
        {
            get
            {
                return Convert.ToInt64(_stringData);
            }
        }

        /// <summary>
        /// Returns this JSONObject as a boolean
        /// </summary>
        public bool Boolean
        {
            get
            {
                return Convert.ToBoolean(_stringData);
            }
        }


        /// <summary>
        /// Prints the JSONObject as a formatted string, suitable for viewing.
        /// </summary>
        public string ToDisplayableString()
        {
            StringBuilder sb = new StringBuilder();
            RecursiveObjectToString(this, sb, 0);
            return sb.ToString();
        }

        #region Private Members

        private string _stringData;
        private JSONObject[] _arrayData;
        private Dictionary<string, JSONObject> _dictData;

        private JSONObject()
        {

        }

        /// <summary>
        /// Recursively constructs this JSONObject 
        /// </summary>
        private static JSONObject Create(object o)
        {
            JSONObject obj = new JSONObject();
            if (o is object[])
            {
                object[] objArray = o as object[];
                obj._arrayData = new JSONObject[objArray.Length];
                for (int i = 0; i < obj._arrayData.Length; ++i)
                {
                    obj._arrayData[i] = Create(objArray[i]);
                }
            }
            else if (o is Dictionary<string, object>)
            {
                obj._dictData = new Dictionary<string, JSONObject>();
                Dictionary<string, object> dict =
                    o as Dictionary<string, object>;
                foreach (string key in dict.Keys)
                {
                    obj._dictData[key] = Create(dict[key]);
                }
            }
            else if (o != null) // o is a scalar
            {
                obj._stringData = o.ToString();
            }

            return obj;
        }

        private static void RecursiveObjectToString(JSONObject obj,
            StringBuilder sb, int level)
        {
            if (obj.IsDictionary)
            {
                sb.AppendLine();
                RecursiveDictionaryToString(obj, sb, level + 1);
            }
            else if (obj.IsArray)
            {
                foreach (JSONObject o in obj.Array)
                {
                    RecursiveObjectToString(o, sb, level);
                    sb.AppendLine();
                }
            }
            else // some sort of scalar value
            {
                sb.Append(obj.String);
            }
        }
        private static void RecursiveDictionaryToString(JSONObject obj,
            StringBuilder sb, int level)
        {
            foreach (KeyValuePair<string, JSONObject> kvp in obj.Dictionary)
            {
                sb.Append(' ', level * 2);
                sb.Append(kvp.Key);
                sb.Append(" => ");
                RecursiveObjectToString(kvp.Value, sb, level);
                sb.AppendLine();
            }
        }


        public static Dictionary<string, string> ParseAllJSON(string s = "")
        {
            JObject jobject = null;
            JArray jarray = null;
            Dictionary<string, string> res = new Dictionary<string, string>();

            JSONObject.Parrent = "";
            JSONObject.DictNum = 0;
            JSONObject.DictDepth = 0;

            if (string.IsNullOrEmpty(s))
            {
                s = @"{""?xml"":{""@version"":""1.0"",""@encoding"":""iso-8859-1""},
                            ""xmlreport"":{""@title"":""ABC: TEST Most Saved2"",""@dates"":""Week of May 19,2013"",
                            ""columns"":{""column"":[{""@name"":""Page"",""@type"":""dimension"",""#text"":""Page""},{""@name"":""Events"",""@type"":""metric"",""@hastotals"":""true"",""#text"":""Events""}]},
                            ""rows"":
                            {""row"":[{""@rownum"":""1"",""cell"":[{""@columnname"":""page"",""@csv"":""\""http://www.ABC.com/profile/recipebox\"""",""#text"":""http://www.ABC.com/profile/recipebox""},{""@columnname"":""events"",""@percentage"":""\""0.1%\"""",""#text"":""489""}]},
                            {""@rownum"":""2"",""cell"":[{""@columnname"":""page"",""@csv"":""\""http://www.ABC.com/recipes/peanut-butter-truffle-brownies/c5c602e4-007b-43e0-aaab-2f9aed89524c\"""",""#text"":""http://www.ABC.com/recip...c602e4-007b-43e0-aaab-2f9aed89524c""},{""@columnname"":""events"",""@percentage"":""\""0.0%\"""",""#text"":""380""}]}]},
                            ""totals"":{""pagetotals"":{""total"":{""@columnname"":""events"",""@value"":""1820.000000"",""#text"":""1,820 (0.2%)""}},
                            ""reporttotals"":{""total"":{""@columnname"":""events"",""@value"":""7838.000000"",""#text"":""7,838 (0.8%)""}},
                            ""timeperiodtotals"":{""total"":{""@columnname"":""events"",""@value"":""955774.000000"",""#text"":""955,774 (100.0%)""}}}}}";

                s = @"[{""c_ischeck"":""1"",""c_zipcode"":""119331"",""c_address_original"":""Москва Кравченко 12"",""c_address_full"":""г Москва, ул Кравченко"",""c_kladr"":""77000000000156600"",""c_region_name"":""Москва"",""c_region_abbr"":""г"",""c_district_name"":"""",""c_district_abbr"":"""",""c_city_name"":"""",""c_city_abbr"":"""",""c_community_name"":"""",""c_community_abbr"":"""",""c_street_name"":""Кравченко"",""c_street_abbr"":""ул"",""c_json_kvant"":{""house"":""12""},""c_house_str"":""дом 12"",""c_addr_lost"":"""",""c_status_error"":""0000"",""c_house_error"":""132"",""c_house_error_desc"":""Дом найден""}]";


                /*     s = @"[{
        ""c_ischeck"": ""1"",
        ""c_zipcode"": ""119331"",
        ""c_address_original"": ""Москва Кравченко 12"",
        ""c_address_full"": ""г Москва, ул Кравченко"",
        ""c_kladr"": ""77000000000156600"",
        ""c_region_name"": ""Москва"",
        ""c_region_abbr"": ""г"",
        ""c_district_name"": """",
        ""c_district_abbr"": """",
        ""c_city_name"": """",
        ""c_city_abbr"": """",
        ""c_community_name"": """",
        ""c_community_abbr"": """",
        ""c_street_name"": ""Кравченко"",
        ""c_street_abbr"": ""ул"",
        ""c_json_kvant"": {
            ""house"": ""12""
        },
        ""c_house_str"": ""дом 12"",
        ""c_addr_lost"": """",
        ""c_status_error"": ""0000"",
        ""c_house_error"": ""132"",
        ""c_house_error_desc"": ""Дом найден""
    }
]";*/
            }

            object result = null;
            try
            {
                result = JsonConvert.DeserializeObject<object>(s);
            }
            catch (System.Exception e)
            {
                Debug.WriteLine("Ошибка десериализации JSON: " + e.Message.ToString());
                return res;
            }
            finally
            {

            }

           // Debug.WriteLine("Получили объект:");
           // Debug.WriteLine(result.ToString());


            if (result is JObject)
            {
                jobject = ((Newtonsoft.Json.Linq.JObject)result);
                //PrintDetail(jobject);
                res = GetKeys(jobject, new Dictionary<string, string>());

            }
            else if (result is JArray)
            {
                jarray = ((Newtonsoft.Json.Linq.JArray)result);
                //PrintDetail(jarray);
                res = GetKeys(jarray, new Dictionary<string, string>());
            }

            return res;
        }

        public static void PrintDetail(JObject node)
        {
            foreach (var item in node)
            {
                Debug.WriteLine("Key: " + item.Key + " Value: " + item.Value);
                if (item.Value is JObject)
                {
                    PrintDetail((JObject)item.Value);
                }
                else if (item.Value is JArray)
                {
                    PrintDetail((JArray)item.Value);
                }

            }
        }



        public static void PrintDetail(JArray node)
        {
            foreach (var item in node)
            {
                Debug.WriteLine("Элемент массива:");
                Debug.WriteLine(item);


                if (item is JObject)
                {
                    PrintDetail((JObject)item);
                }
                else if (item is JArray)
                {
                    PrintDetail((JArray)item);
                }
            }
        }

        public static Dictionary<string, string> GetKeys<T>(T node, Dictionary<string, string> ret = null)
        {
            JSONObject.DictDepth += 1;
            if (ret == null)
            {
                ret = new Dictionary<string, string>();
            }

            if ("JObject" == typeof(T).Name)
            {
           
                foreach (var item in node as JObject)
                {
                    //Debug.WriteLine("Key: " + item.Key + " Value: " + item.Value);
                    JSONObject.DictNum += 1;

                    if (string.IsNullOrEmpty(JSONObject.Parrent) && JSONObject.DictDepth == 1)
                    {
                        ret.Add(item.Key.ToString() + "_#_" + JSONObject.DictNum.ToString(), item.Value.ToString());
                    }
                    else if (string.IsNullOrEmpty(JSONObject.Parrent) && JSONObject.DictDepth != 1)
                    {
                        ret.Add(item.Key.ToString() + "_#_" + JSONObject.DictNum.ToString() , item.Value.ToString());
                    }
                    else if (!string.IsNullOrEmpty(JSONObject.Parrent) && JSONObject.DictDepth != 1)
                    {
                        ret.Add(JSONObject.Parrent + "_||_" + item.Key.ToString() + "_#_" + JSONObject.DictNum.ToString(), item.Value.ToString());
                    }
                    else if (!string.IsNullOrEmpty(JSONObject.Parrent) && JSONObject.DictDepth == 1)
                    {
                        JSONObject.Parrent = "";
                        ret.Add(item.Key.ToString() + "_#_" + JSONObject.DictNum.ToString() , item.Value.ToString());
                    }
                    if (item.Value is JObject)
                    {
                        JSONObject.Parrent = item.Key.ToString();
                        ret = GetKeys((JObject)item.Value, ret);
                    }
                    else if (item.Value is JArray)
                    {                     
                        JSONObject.Parrent = item.Key.ToString();
                        ret = GetKeys((JArray)item.Value, ret);
                    }

                }
                JSONObject.DictDepth -= 1;
                JSONObject.Parrent = "";
                return ret;
            }
            else if ("JArray" == typeof(T).Name )
            {
               
                foreach (var item in node as JArray)
                {
                    //Debug.WriteLine("Array: " + item );

                    // ret.Add(item.Key.ToString(), item.Value.ToString());

                    if (item is JObject)
                    {
                        ret = GetKeys((JObject)item, ret);
                    }
                    else if (item is JArray)
                    {
                        ret = GetKeys((JArray)item, ret);
                    }

                }
                return ret;
            }
            return ret;    
        }
        /*
        public static Dictionary<string, string> GetKeys(JArray node, Dictionary<string, string> ret = null)
        {
            if (ret == null)
            {
                ret = new Dictionary<string, string>();
            }

            foreach (var item in node)
            {
                //Debug.WriteLine("Array: " + item );

                // ret.Add(item.Key.ToString(), item.Value.ToString());

                if (item is JObject)
                {
                    ret = GetKeys((JObject)item, ret);
                }
                else if (item is JArray)
                {
                    ret = GetKeys((JArray)item, ret);
                }

            }
            return ret;
        }
        */

        public static Dictionary<string, object> deserializeToDictionary(string jo)
        {
            var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
            var values2 = new Dictionary<string, object>();
            foreach (KeyValuePair<string, object> d in values)
            {
                if (d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
                {
                    values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
                }
                else
                {
                    values2.Add(d.Key, d.Value);
                }
            }
            return values2;
        }

        
        #endregion

    }
}
